합성곱 출력 크기 계산
합성곱 출력 크기는 입력 벡터 위를 필터 w가 이동하는 전체 횟수로 결정된다.입력벡터의 크기: n, 필터의 크기: m, 패딩: p, 스트라이드: s
x*w의 출력 크기는
o=[(n+2p-m)/s]+1 [] 안의 계산 결과는 버림
이다.
1차원 합성곱 계산
import numpy as np
def conv1d(x, w, p=0, s=1):
w_rot=np.array(w[::-1])
x_padded=np.array(x)
if p>0:
zero_pad=np.zeros(shape=p)
x_padded=np.concatenate([zero_pad, x_padded, zero_pad])
res=[]
for i in range(0, int((len(x_padded)-len(w_rot))/s+1), s):
res.append(np.sum(x_padded[i:i+w_rot.shape[0]]*w_rot))
return np.array(res)
test
x=[1, 3, 2, 4, 5, 6, 1, 3]
w=[1, 0, 3, 1, 2]
print('Conv1d implements:', conv1d(x, w, p=2, s=1))
print('numpy results:', np.convolve(x, w, mode='same'))
Conv1d implements: [ 5. 14. 16. 26. 24. 34. 19. 22.]
numpy results: [ 5 14 16 26 24 34 19 22]
2차원 이산 합성곱 연산
X_n1xn2, W_m1xm2 m1<n1, m2<=n2
1차원 합성곱에서 차원만 하나 늘어 났을 뿐 계산은 동일하다.
제로 패딩, 필터 행렬의 회전, 스트라이드 같은 모든 기법도 2D에 적용할 수 있다.
n: 8x8, m: 3x3, p=1, s=1일 때 출력은 8x8
p=(1, 1), s=(2, 2)일 때, 입력행렬X(3x3)과 커널행렬W(3x3)은
X의 네 면에 0이 한줄씩 추가되어 5x5 행렬이 된다.
W를 뒤집으면 단순히 행렬의 전치가 아닌 W_rot=W[::-1, ::-1]처럼 역전된 행렬이 된다.
s=2일 때 출력의 크기는
o=[n+2p-m/s]+1 []안의 계산 결과는 버림
3x3이다.
scipy.signal은 2차원 합성곱을 계산할 수 있는 scipy.signal.convolve2d함수를 제공한다.
import numpy as np
import scipy.signal
def conv2d(X, W, p=(0, 0), s=(1, 1)):
W_rot=np.array(W)[::-1, ::-1]
X_orig=np.array(X)
n1=X_orig.shape[0]+2*p[0]
n2=X_orig.shape[1]+2*p[1]
X_padded=np.zeros(shape=(n1, n2))
X_padded[p[0]:p[0]+X_orig.shape[0], p[1]:p[1]+X_orig.shape[1]]=X_orig
res=[]
for i in range(0, int((X_padded.shape[0]-W_rot.shape[0])/s[0])+1, s[0]):
res.append([])
for j in range(0, int((X_padded.shape[1]-W_rot.shape[1])/s[1])+1, s[1]):
X_sub=X_padded[i:i+W_rot.shape[0], j:j+W_rot.shape[1]]
res[-1].append(np.sum(X_sub*W_rot))
return (np.array(res))
test
X=[[1, 3, 2, 4], [5, 6, 1, 3], [1, 2, 0, 2], [3, 4, 3, 2]]
W=[[1, 0, 3], [1, 2, 1], [0, 1, 1]]
print('Conv2d implements:\n', conv2d(X, W, p=(1, 1), s=(1, 1)))
print('scifi resutls: \n', scipy.signal.convolve2d(X, W, mode='same'))
Conv2d implements:
[[11. 25. 32. 13.]
[19. 25. 24. 13.]
[13. 28. 25. 17.]
[11. 17. 14. 9.]]
scifi resutls:
[[11 25 32 13]
[19 25 24 13]
[13 28 25 17]
[11 17 14 9]]
위의 합성꼽 계산은 메모리 효율과 계산 복잡도 측면에서 매우 비효율적이기 때문에 신경망 애플리케이션에서 사용할 수 없다.
텐서플로 같은 대부분의 라이브러리는 필터 행렬을 뒤집지 않는다.
푸리에(Fourier)변환을 사용해서 합성곱을 계산하는 효율적인 알고리즘을 개발하였고, 이를 이용한다.
또한 신경망 알고리즘에서 사용하는 합성곱 커널의 크기는 일반적으로 입력 이미지보다 훨씬 작다.
일반적으로 CNN에서 1x1, 3x3, 5x5 크기의 커널을 사용한다.
이런 합성곱 연산을 효율적으로 수행하는 위노그라드 최솟값 필터링(Winograd’s Minimal Filtering) 알고리즘이 개발되어 있다.